/*
 * @Autor: PK
 * @Date: 2021-03-31 13:34:14
 * @Description: 反射工具类，用来反射设置属性，仿真中请不要使用反射相关方法，仅在初始化时方便设置时使用
 */
using System.Collections.Generic;
using System;
using System.Reflection;
using System.Linq;
using ShkSimulation.core.exceptions;
using System.Data;
using ShkSimulation.core.template.agency;

namespace ShkSimulation.core.util
{
    public static class RelectionUtil{

        /**
         * @description: 通过反射设置属性或字段
         * @param 字段字典，字段名:值
         */
        public static void SetPropertyAndField(Object item, Dictionary<string, object> attributes, ISet<string> include = null, ISet<string> exclude = null) {
            Type type = item.GetType();
            Dictionary<string, PropertyInfo> propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Distinct().ToDictionary(each => each.Name);
            Dictionary<string, FieldInfo> fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Distinct().ToDictionary(each => each.Name);
            foreach (var each in attributes) {
                var name = each.Key;
                if (exclude != null && exclude.Contains(name)) {
                    continue;
                }
                if (propertyInfos.ContainsKey(name)) {
                    propertyInfos[name].SetValue(item, attributes[name]);
                    continue;
                }
                if (fieldInfos.ContainsKey(name)) {
                    fieldInfos[name].SetValue(item, attributes[name]);
                    continue;
                }
                if (include != null && include.Contains(name)) {
                    throw new PropertyAttributeMissingException("生成属性" + name + "出错，配置文件未设置");
                }
            }
        }
        /**
        * @description: 通过反射设置属性或字段， 对于不存在的就放在map中
         * @param 字段字典，字段名:值
         */
        public static T CreateAgency<T>(T agency, string[] headers, DataRow roadLine, Dictionary<string, object> attributes) where T: Agency{
            Type type = agency.GetType();
            Dictionary<string, PropertyInfo> propertyInfo = new Dictionary<string, PropertyInfo>();
            foreach (var item in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
                string propertyName = item.Name;
                if (propertyInfo.ContainsKey(propertyName)) {
                    PropertyInfo infoInMap = propertyInfo[propertyName];
                    if (item.DeclaringType.IsSubclassOf(infoInMap.DeclaringType)) {
                        propertyInfo[propertyName] = item;
                    }
                } else {
                    propertyInfo.Add(propertyName, item);
                }
            }
            Dictionary<string, FieldInfo> fieldInfo = new Dictionary<string, FieldInfo>();
            foreach (var item in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
                string fieldName = item.Name;
                if (fieldInfo.ContainsKey(fieldName)) {
                    FieldInfo infoInMap = fieldInfo[fieldName];
                    if (item.DeclaringType.IsSubclassOf(infoInMap.DeclaringType)) {
                        fieldInfo[fieldName] = item;
                    }
                } else {
                    fieldInfo.Add(fieldName, item);
                }
            }
            foreach (var fieldName in headers) {
                var value = roadLine[fieldName];
                if (propertyInfo.ContainsKey(fieldName)) {
                    value = Convert.ChangeType(value, propertyInfo[fieldName].PropertyType);
                    PropertyInfo info = propertyInfo[fieldName];
                    info.SetValue(agency, value, null);
                }
                if (fieldInfo.ContainsKey(fieldName)) {
                    fieldInfo[fieldName].SetValue(agency, value);
                    continue;
                }
                if (!attributes.ContainsKey(fieldName)) {
                    attributes.Add(fieldName, value);
                } else {
                    Console.WriteLine("设置属性时，存在重复属性/字段" + fieldName);
                }
            }
            return agency;
        }
    }
}
